package math;

/**
 *
 * @author Luan
 */
public class Line {
    public float a, b, c;
    //Coeficientes da equação ax + by + c = 0 da reta
    
    public Line(float a, float b, float c) {
        this.a = a;
        this.b = b;
        this.c = c;
        
        this.simplificar();
    }
    
    //y = mx + q
    public Line(float m, float q) {
        this(m, -1, q);
    }
    
    //q = y - mx
    public Line(float m, Point p) {
        this(m, p.y - m*p.x);
    }
    
    //m = dy / dx
    public Line(Point p1, Point p2) {
        if (p1.x == p2.x) {
            this.a = 1;
            this.b = 0;
            this.c = -p1.x;
        } else {
            this.a = (p1.y - p2.y)/(p1.x - p2.x);
            this.b = -1;
            this.c = p2.y - this.a*p2.x;
        }
    }
    
    private void simplificar() {
        float t = (float) Math.floor(Math.sqrt(Math.min(Math.min(a, b), c))) + 1;
        for (int i = 0; i < t; i++)
            while (a % i == 0 && b % i == 0 && c % i == 0) {
                a /= i;
                b /= i;
                c /= i;
            }
        
        //yes - you need to do this
        if (a == -0)
            a = 0;
        if (b == -0)
            b = 0;
        if (c == -0)
            c = 0;
    }
    
    public float getM() {
        return -a/b;
    }
    
    public float getQ() {
        return -c/b;
    }
    
    public float solve(float x) {
        return getYFor(x);
    }
    
    public float getYFor(float x) {
        if (b == 0)
            return -c/a; //Retas verticais
        return -(a*x + c)/b;
    }
    
    public float getXFor(float y) {
        return -(b*y + c)/a;
    }
    
    public boolean contem(Point p) {
        return solve(p.x) == p.y;
    }
    
    public Point intersecta(Line r) {
        float x = (r.getQ() - this.getQ())/(this.getM() - r.getM());
        return new Point(x, this.solve(x));
    }
    
    public Point closestPoint(Point p) {
        return this.intersecta(new Line(-1/(getM()), p));
    }
    
    public Point equidistante(Point s, Point r) {
        float m = getM(), q = getQ();
        float x = (-2*q*r.y + 2*q*s.y + r.x*r.x + r.y*r.y - s.x*s.x - s.y*s.y)/(2*m*r.y - 2*m*s.y + 2*r.x - 2*s.x);
        return new Point(x, solve(x));
    }
    
    public float dist(Point p) {
        return closestPoint(p).dist(p);
    }
    
    //Retorna o primeiro ponto de opcoes que estiver entre o início e o fim
    //Retorna null se não há opções válidas
    public static Point bordaDe(Point[] opcoes, Point inicio, Point fim) {
        for (int i = 0; i < opcoes.length; i++)
            if (Point.entre(inicio, fim, opcoes[i]))
                return opcoes[i];
        System.err.println("Não foi possível colocar os pontos " + opcoes[0] + " e " + opcoes[1] + "no meio de " + inicio + " e " + fim);
        Point.entre(inicio, opcoes[0], fim);
        return null;
    }
    
    //Retorno os dois pontos dessa reta cuja distância até start é dist
    public Point[] whereDist(Point A, float dist) {
        if (b == 0) //reta vertical
            return new Point[] { new Point(-c/a, A.y - dist), new Point(-c/a, A.y + dist) };
        
        float m = getM(), q = getQ();
        
        float delta = -A.x*A.x*m*m + 2*A.x*A.y*m - 2*A.x*m*q - A.y*A.y + 2*A.y*q + dist*dist*m*m + dist*dist - q*q;
        if (delta < 0)
            return null;
        delta = (float) Math.sqrt(delta);
        float a1 = A.x + A.y*m - m*q;
        float a2 = m*m + 1;
        float x1 = (delta + a1)/a2;
        float x2 = (-delta + a1)/a2;
        
        float y1 = solve(x1), y2 = solve(x2);
        
        return new Point[] { new Point(x1, y1), new Point(x2, y2) };
    }
    
    //Retorna a reta perpendicular a esta que passa pelo ponto p
    public Line getRetaPerpendicular(Point p) {
        return new Line(-1/getM(), p);
    }
    
    public boolean ePerpendicular(Line r) {
        return getM()*r.getM() == -1;
    }
    
    @Override
    public String toString() {
        return Point.toString(a) + "x + " + Point.toString(b) + "y + " + Point.toString(c) + " = 0";
    }
}
